package com.klikmee.sms.core.base.postman;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Date;

import javax.xml.ws.BindingProvider;

import org.apache.log4j.Logger;

import com.klikmee.sms.cef.services.ws.ServiceInterface;
import com.klikmee.sms.cef.services.ws.ServiceInterfaceService;
import com.klikmee.sms.cef.services.ws.ServiceRequestMessage;
import com.klikmee.sms.cef.services.ws.ServiceResponseReport;
import com.klikmee.sms.cef.services.ws.SubscriptionInfo;
import com.klikmee.sms.core.base.util.CoreUtilities;
import com.klikmee.sms.core.base.util.States;
import com.klikmee.sms.core.commons.exceptions.ErrorInfo;
import com.klikmee.sms.core.model.IncomingMessage;
import com.klikmee.sms.core.model.OutgoingMessage;
import com.klikmee.sms.core.model.Service;

public class SimpleMessageDeliverer implements ServiceMessageDeliverer {

	/**
	 * URL of the WSDL file for the ws that every sms service instance
	 * implements to communicate to core platform
	 */
	private URL					wsdlURL			= null;

	/**
	 * Stub through which to communicate to service
	 */
	private ServiceInterface	serviceProxy	= null;

	/**
	 * Logger instance
	 */
	private Logger				logger			= Logger.getLogger( SimpleMessageDeliverer.class );

	/**
	 * This method initializes message deliverer.
	 * 
	 */
	public void init() {
		// instantiate sms service ws client stub
		serviceProxy = new ServiceInterfaceService( this.wsdlURL ).getServiceInterfacePort();
		logger.info( "Simple message deliverer initialized." );
	}

	/**
	 * Tries to deliver message to the service. Service endpoint address is
	 * validated first.
	 * 
	 * @param incomingMessage
	 *            message to be delivered to service
	 * @return true if message is successfully delivered, otherwise false
	 * @throws InvalidServiceUrlException
	 */
	public boolean deliverMessage(IncomingMessage incomingMessage) throws InvalidServiceUrlException {

		// validate URL
		String endpointURL = incomingMessage.getService().getEndpoint();
		try {
			validateURL( endpointURL );
		}
		catch (MalformedURLException e1) {
			// validation failed, throw exceptoin
			InvalidServiceUrlException invalidServiceUrlException = InvalidServiceUrlException.create( "Invalid service URL while delivering incoming message", "MessageDeliverer/ForwardIncomingMessage" );
			ErrorInfo errorInfo = invalidServiceUrlException.addInfo();
			errorInfo.getParameters().put( "URL", incomingMessage.getService().getEndpoint() );
			errorInfo.getParameters().put( "Incoming message id", incomingMessage.getId() );

			throw invalidServiceUrlException;
		}
		catch (URISyntaxException e1) {
			// validation failed, throw exceptoin
			InvalidServiceUrlException invalidServiceUrlException = InvalidServiceUrlException.create( "Invalid service URL while delivering incoming message", "MessageDeliverer/ForwardIncomingMessage" );
			ErrorInfo errorInfo = invalidServiceUrlException.addInfo();
			errorInfo.getParameters().put( "URL", incomingMessage.getService().getEndpoint() );
			errorInfo.getParameters().put( "Incoming message id", incomingMessage.getId() );

			throw invalidServiceUrlException;
		}
			

		// override addres in proxy
		overrideEndpointAddress( endpointURL );

		// deliver message to service
		ServiceRequestMessage serviceRequestMessage = this.createServiceRequestMessage( incomingMessage );

		try {
			serviceProxy.onRequestMessage( serviceRequestMessage );
			logger.info( MessageFormat.format( "Delivered message with id [{0}] to service name = [{1}]", incomingMessage.getId(), incomingMessage.getService().getName() ) );
			// if everything OK, return true
			return true;
		}
		catch (Exception e) {
			logger.fatal( "Failed to deliver message to service", e );
			return false;
		}

	}

	public ServiceRequestMessage createServiceRequestMessage(IncomingMessage incomingMessage) {
		ServiceRequestMessage message = new ServiceRequestMessage();
		message.setCoreId( incomingMessage.getId() );
		message.setFromNumber( incomingMessage.getFromNumber() );
		message.setMessageText( incomingMessage.getMessage() );
		message.setTimeReceived( CoreUtilities.convertToXMLGregorianCalendar( incomingMessage.getOurIncomingTime() ) );
		message.setToNumber( incomingMessage.getToNumber() );

		return message;

	}

	private void overrideEndpointAddress(String newEndpointAddress) {
		BindingProvider provider = (BindingProvider) serviceProxy;
		provider.getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, newEndpointAddress );
	}

	public boolean validateURL(String address) throws MalformedURLException, URISyntaxException {
		// try to construct URL out of address
			URL url = new URL( address );
			// if this is ok check more
			url.toURI();
			// if no exceptions are raised, return true
			logger.debug( MessageFormat.format( "Url validation succeeded for url [{0}]", address ));
			return true;
	}

	/**
	 * Forwards status report to
	 * 
	 * @param outgoingMessage
	 *            obj containing status report data
	 * @throws InvalidServiceUrlException 
	 */
	public boolean forwardDeliveryReport(OutgoingMessage outgoingMessage) throws InvalidServiceUrlException {

		String endpoint = outgoingMessage.getService().getEndpoint();

		// if end point URL is null then skip
		if (endpoint == null) {
			logger.info( MessageFormat.format( "Service [{0}] endpoint undefined, skipping delivery report forwarding.", outgoingMessage.getService().getName() ) );
			return true;
		}
		// validate URL
		try {
			validateURL( endpoint );
		}
		catch (MalformedURLException e1) {
			// validation failed, throw exceptoin
			InvalidServiceUrlException invalidServiceUrlException = InvalidServiceUrlException.create( "Invalid service URL while delivering status report", "MessageDeliverer/ForwardStatusReport" );
			ErrorInfo errorInfo = invalidServiceUrlException.addInfo();
			errorInfo.getParameters().put( "URL", outgoingMessage.getService().getEndpoint() );
			errorInfo.getParameters().put( "Outgoing message id", outgoingMessage.getId() );

			throw invalidServiceUrlException;
		}
		catch (URISyntaxException e1) {
			// validation failed, throw exceptoin
			InvalidServiceUrlException invalidServiceUrlException = InvalidServiceUrlException.create( "Invalid service URL while delivering status report", "MessageDeliverer/ForwardStatusReport" );
			ErrorInfo errorInfo = invalidServiceUrlException.addInfo();
			errorInfo.getParameters().put( "URL", outgoingMessage.getService().getEndpoint() );
			errorInfo.getParameters().put( "Outgoing message id", outgoingMessage.getId() );

			throw invalidServiceUrlException;
		}
			

		// build ResponseReport obj
		ServiceResponseReport responseReport = createServiceResponseReport( outgoingMessage );

		overrideEndpointAddress( endpoint );

		// try to deliver report
		try {
			serviceProxy.onResponseReport( responseReport );
			logger.info( MessageFormat.format( "Delivered status report to service name = [{0}], message id = [{1}]", outgoingMessage.getService().getName(), outgoingMessage.getId() ) );
			return true;
		}
		catch (Exception e) {
			logger.fatal( String.format( "Failed to deliver status report to service, outgoingmessage id = [%s]", outgoingMessage.getId() ), e );
			return false;
		}

	}

	private ServiceResponseReport createServiceResponseReport(OutgoingMessage outgoingMessage) {
		ServiceResponseReport report = new ServiceResponseReport();
		report.setResponseId( outgoingMessage.getId() );
		report.setStatus( outgoingMessage.getState().equals( States.OutgoingMessage.DELIVERED ) ? "DELIVERED" : "NOT DELIVERED" );

		return report;
	}

	/**
	 * Forwards approved subscription request to service instance.
	 */
	public boolean forwardSubscripitonRequest(Service service, String distributionListId, String msisdn, Date validTo) {

		String endpoint = service.getEndpoint();
		// if enpoint is null then skip
		if (endpoint == null) {
			return true;
		}

		try {
			validateURL( endpoint );
		}
		catch (MalformedURLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		catch (URISyntaxException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		// build subscription info
		SubscriptionInfo subscriptionInfo = createSubscriptionInfo( distributionListId, msisdn, validTo );

		try {
			serviceProxy.onSubscriptionApproved( subscriptionInfo );
			return true;
		}
		catch (Exception e) {
			logger.fatal( String.format( "Failed to forward subscription request to service, distributionListId = [%s], msisdn = [%s] ", distributionListId, msisdn ), e );
			return false;
		}

	}

	/**
	 * Utility method for construction {@link SubscriptionInfo}
	 * 
	 * @param distributionListId
	 * @param msisdn
	 * @param validTo
	 * @return
	 */
	private SubscriptionInfo createSubscriptionInfo(String distributionListId, String msisdn, Date validTo) {
		SubscriptionInfo subscriptionInfo = new SubscriptionInfo();
		subscriptionInfo.setDistributionListId( distributionListId );
		subscriptionInfo.setMsisdn( msisdn );
		subscriptionInfo.setValidTo( CoreUtilities.convertToXMLGregorianCalendar( validTo ) );

		return subscriptionInfo;

	}

	public void setWsdlURL(URL wsdl) {
		this.wsdlURL = wsdl;
	}

}
